#include <string>
#include <iostream>
#include <sstream>
#include <locale>
#include <boost/locale.hpp>
#include <boost/dll/shared_library.hpp>
#include <boost/filesystem.hpp>
#include <boost/exception/all.hpp>
#include <boost/process.hpp>
#ifdef _WIN32
#include <boost/process/windows.hpp>
#endif
#include <engine.h>

# define _snprintf snprintf
#include <stdio.h>
#ifdef _WIN32
#define strcasecmp stricmp
#else
#include <strings.h>
#endif

#include <Python.h>
using namespace std;
using namespace boost;

string invoke_module(string file, string param="");
string invoke_module(string file, string param)
{
    try {
        dll::shared_library lib;
        boost::filesystem::path file_path(file);
        if (strcasecmp(file.c_str() + file.size() - 3, ".so") == 0 || 
            strcasecmp(file.c_str() + file.size() - 4, ".dll") == 0) {
            lib.load(file_path);
            string func_name = file_path.filename().string();
            if(!lib.has(func_name)) {
                return "can not find interface export:"+func_name;
            }
            string(*func)(string&) = lib.get<string(string&)>(func_name);
            string textOut = func(param);
            lib.unload();
            return textOut;
        }
        else if (strcasecmp(file.c_str() + file.size() - 2, ".m") == 0) {
            char textOut[1024] = {};
            file_path.replace_extension("");
            Engine* engine = engOpen(0);
            engOutputBuffer(engine,textOut,sizeof(textOut));
            engEvalString(engine,("addpath('"+file_path.parent_path().string()+"')").c_str());
            engEvalString(engine,(file_path.filename().string()+"('"+param+"')").c_str());
            engClose(engine);
            return textOut;
        }
        else if(strcasecmp(file.c_str() + file.size() - 3, ".py") == 0) {
            string line,command;
            std::error_code ec;
            process::ipstream is;
#ifdef _WIN32
            process::system("where python", process::std_out>is, 
                ec,process::windows::create_no_window);
#else
            process::child c("whereis python3", process::std_out>is);
            c.wait();
#endif
            std::getline(is, line);
            Py_SetPythonHome(boost::locale::conv::to_utf<wchar_t>(
                boost::filesystem::path(line).parent_path().string(), "gbk").c_str());
            cout << "python path:" << boost::filesystem::path(line).parent_path().string() <<endl;
            Py_Initialize();
            if(!Py_IsInitialized())
                return "init python error";
            
            string textOut;
            PyRun_SimpleString(("import sys\nsys.path.append(r'"+boost::filesystem::path(file).parent_path().string()+"')").c_str());
            PyObject* pyModule = PyImport_ImportModuleNoBlock(boost::filesystem::path(file).stem().string().c_str());
            if (pyModule) {
                PyObject* pyFunc = PyObject_GetAttrString(pyModule, boost::filesystem::path(file).stem().string().c_str());
                if (pyFunc) {
                    if (PyCallable_Check(pyFunc) != 0){
                        PyObject* result = NULL;
                        result = PyObject_CallFunction(pyFunc,param.c_str());
                        textOut = (char*)PyUnicode_DATA(PyObject_Str(result));
                        if(result) Py_DECREF(result);
                    }
                    Py_DECREF(pyFunc);
                }
                Py_DECREF(pyModule);
            }
            PyErr_Print();
            PyErr_Clear();
            if(Py_IsInitialized())
                Py_Finalize();
            return textOut;
        }
        else {
            return "unknow file type,"+file;
        }
        return "";
    }
    catch (boost::exception &e) {
        return boost::diagnostic_information(e);
    }
}